package system

import (
	"MoSkeleton/framework/core/models"
	"MoSkeleton/framework/core/moerrors"
	"MoSkeleton/framework/core/motypes"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/base"
	"database/sql"
	"errors"
	"gorm.io/gorm"
	"strconv"
)

const (
	SU_Simple_Columns = "id,dept_id,user_name,nick_name,user_type,email,phonenumber,sex,avatar,password,salt,login_date"
)

type SysUser struct {
	models.MoModel
	models.MoUserModel
	DeptId      *uint64         `gorm:"column:dept_id;type:BIGINT(20);"`
	UserName    string          `gorm:"column:user_name;type:VARCHAR(30);NOT NULL"`
	NickName    string          `gorm:"column:nick_name;type:VARCHAR(30);NOT NULL"`
	UserType    string          `gorm:"column:user_type;type:VARCHAR(2);"`
	Email       sql.NullString  `gorm:"column:email;type:VARCHAR(50);"`
	Phonenumber sql.NullString  `gorm:"column:phonenumber;type:VARCHAR(11);"`
	Sex         sql.NullString  `gorm:"column:sex;type:CHAR(1);"`
	Avatar      sql.NullString  `gorm:"column:avatar;type:VARCHAR(100);"`
	Password    string          `gorm:"column:password;type:VARCHAR(100);"`
	Salt        string          `gorm:"column:salt;type:VARCHAR(20);"`
	Status      string          `gorm:"column:status;type:CHAR(1);"`
	DelFlag     string          `gorm:"column:del_flag;type:CHAR(1);"`
	LoginIp     string          `gorm:"column:login_ip;type:VARCHAR(128);"`
	LoginDate   *motypes.MoTime `gorm:"column:login_date;type:DATETIME;"`
	Remark      sql.NullString  `gorm:"column:remark;type:VARCHAR(500);"`
}

func (s *SysUser) IsAdmin() bool {
	return IsUserAdmin(s.ID)
}

func IsUserAdmin(userId uint64) bool {
	return userId == 1
}

func HasAdmin(userIds *[]uint64) bool {
	for _, id := range *userIds {
		if IsUserAdmin(id) {
			return true
		}
	}
	return false
}

func HasAdminAsStr(userIds *[]string) bool {
	for _, id := range *userIds {
		userId, _ := strconv.ParseUint(id, 10, 64)
		if IsUserAdmin(userId) {
			return true
		}
	}
	return false
}

func (s *SysUser) TableName() string {
	return "sys_user"
}

func (s *SysUser) GetWithPasswordByUsername(username string) (bool, error) {
	result := base.BaseDb.Where("user_name=? and del_flag=0 and status=0", username).Select(SU_Simple_Columns).First(s)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return false, nil
		}
		return false, result.Error
	}
	return true, nil
}

func (s *SysUser) GetWithPasswordById(id uint64) (bool, error) {
	result := base.BaseDb.Where("id=? and del_flag=0 and status=0", id).Select(SU_Simple_Columns).First(s)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return false, nil
		}
		return false, result.Error
	}
	return true, nil
}

func (s *SysUser) DeleteByIds(tx *gorm.DB, ids *[]uint64) (bool, error) {
	if len(*ids) == 0 {
		return true, nil
	}
	result := base.GetModelDB(tx).Model(s).Where("id in (?)", *ids).Update("del_flag", "2")
	return result.RowsAffected > 0, result.Error
}

func (s *SysUser) CheckUsernameUnique() (bool, error) {
	if s.UserName == "" {
		return true, nil
	}
	var oldUser SysUser
	result := base.BaseDb.Model(&SysUser{}).Where("user_name = ?", s.UserName).First(&oldUser)
	return s.checkUnique(result, &oldUser)
}

func (s *SysUser) GetIdByUsername(username string) (uint64, error) {
	if username == "" {
		return 0, moerrors.NewWithMsg("用户名不能为空")
	}
	var id uint64
	result := base.GetModelDB(nil).Model(&SysUser{}).Where("user_name = ?", username).Select("id").Find(&id)
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return 0, result.Error
	}
	return id, nil
}

func (s *SysUser) CheckEmailUnique() (bool, error) {
	if !s.Email.Valid || s.Email.String == "" {
		return true, nil
	}
	var oldUser SysUser
	result := base.GetModelDB(nil).Model(&SysUser{}).Where("email = ?", s.Email).First(&oldUser)
	return s.checkUnique(result, &oldUser)
}

func (s *SysUser) CheckPhoneUnique() (bool, error) {
	if !s.Phonenumber.Valid || s.Phonenumber.String == "" {
		return true, nil
	}
	var oldUser SysUser
	result := base.GetModelDB(nil).Model(&SysUser{}).Where("phonenumber = ?", s.Phonenumber).First(&oldUser)
	return s.checkUnique(result, &oldUser)
}

func (s *SysUser) GenPwdForNew(pwd string) {
	s.Salt = utils.GenPwdSalt()
	s.Password = utils.HashPasswd(pwd, s.Salt)
}

func (s *SysUser) checkUnique(dbResult *gorm.DB, oldUser *SysUser) (bool, error) {
	if dbResult.Error != nil {
		if errors.Is(dbResult.Error, gorm.ErrRecordNotFound) {
			return true, nil
		}
		return false, dbResult.Error
	}
	if s.ID > 0 && oldUser.ID == s.ID {
		return true, nil
	}
	return false, nil
}
